/*
 * Copyright 2008 - 2010 Lars Heuer (heuer[at]semagia.com). All rights reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package com.semagia.atomico.server.feed.impl;

import java.io.IOException;
import java.io.OutputStream;
import java.net.URI;
import java.util.List;

import com.semagia.atomico.MediaType;
import com.semagia.atomico.dm.IAuthor;
import com.semagia.atomico.dm.IAuthorAwareEntity;
import com.semagia.atomico.dm.IEntity;
import com.semagia.atomico.dm.ISummaryAwareEntity;
import com.semagia.atomico.server.IConfiguration;
import com.semagia.atomico.server.UnsupportedMediaTypeException;
import com.semagia.atomico.server.dm.ICollectionInfo;
import com.semagia.atomico.server.dm.IVariantAwareWritableRepresentation;
import com.semagia.atomico.server.dm.IFragmentInfo;
import com.semagia.atomico.server.dm.ISnapshotInfo;
import com.semagia.atomico.server.feed.IOutputAwareFeedHandler;
import com.semagia.atomico.server.storage.IStorage;
import com.semagia.atomico.server.storage.StorageException;
import com.semagia.atomico.server.utils.LinkUtils;
import com.semagia.atomico.server.utils.MediaTypeUtils;

/**
 * Factory for feeds.
 * 
 * @author Lars Heuer (heuer[at]semagia.com) <a href="http://www.semagia.com/">Semagia</a>
 * @version $Rev: 132 $ - $Date: 2012-07-20 16:55:23 +0000 (Fri, 20 Jul 2012) $
 */
public final class FeedFactory {

    public FeedFactory() {
        // noop.
    }

    public IVariantAwareWritableRepresentation createOverviewFeed(URI baseURI,
            final IStorage storage, final IConfiguration config, 
            final long lastModification,
            final List<MediaType> feedMediaTypes) throws StorageException, UnsupportedMediaTypeException {
        final OverviewFeed feed = new OverviewFeed (
                baseURI.toASCIIString(), config.getOverviewFeedTitle(),
                lastModification);
        applyAuthorInfo(config, feed);
        for (ICollectionInfo info: storage.getCollectionInfos(config.getCollectionSortOrder())) {
            feed.addEntry(info, LinkUtils.linkToCollection(baseURI, info));
        }
        final MediaType mediaType = getFeedMediaType(feedMediaTypes);
        final IOutputAwareFeedHandler handler = makeFeedHandler(mediaType);
        return new FeedWritable(new OverviewFeedWriter(feed, handler), mediaType);
    }

    public IVariantAwareWritableRepresentation createCollectionFeed(final URI baseURI,
            final IStorage storage, final IConfiguration config, 
            final ICollectionInfo collInfo, 
            final List<MediaType> feedMediaTypes) throws StorageException, UnsupportedMediaTypeException {
        final String collTitle = collInfo.getTitle();
        final String collId = collInfo.getCollectionId();
        final CollectionFeed feed = new CollectionFeed(baseURI.toASCIIString(), 
                                config.getCollectionFeedTitle(collTitle),
                                collInfo.getUpdated());
        applyAuthorInfo(config, feed, collInfo);
        if (collInfo instanceof ISummaryAwareEntity) {
            feed.setSummary(((ISummaryAwareEntity) collInfo).getSummary());
        }
        feed.setSnapshotsEntry(LinkUtils.linkToSnapshots(baseURI, collInfo).toASCIIString(),
                                config.getSnapshotsEntryTitle(collTitle),
                                storage.lastSnapshotModification(collId));
        feed.setFragmentsEntry(LinkUtils.linkToFragments(baseURI, collInfo).toASCIIString(), 
                                config.getFragmentsEntryTitle(collTitle),
                                storage.lastFragmentModification(collId));
        final MediaType mediaType = getFeedMediaType(feedMediaTypes);
        final IOutputAwareFeedHandler handler = makeFeedHandler(mediaType);
        return new FeedWritable(new CollectionFeedWriter(feed, handler), mediaType);
    }

    public IVariantAwareWritableRepresentation createFragmentsFeed(final URI baseURI,
            final IStorage storage, final IConfiguration config, 
            final long lastModification, final ICollectionInfo collInfo,
            List<MediaType> feedMediaTypes) throws StorageException, UnsupportedMediaTypeException {
        final FragmentsFeed feed = new FragmentsFeed(baseURI.toASCIIString(), 
                    config.getFragmentFeedTitle(collInfo.getTitle()),
                    lastModification);
        applyAuthorInfo(config, feed, collInfo);
        for (IFragmentInfo info: storage.getFragmentInfos(collInfo.getCollectionId())) {
            feed.addEntry(info, LinkUtils.linkTo(baseURI, collInfo, info));
        }
        final MediaType mediaType = getFeedMediaType(feedMediaTypes);
        final IOutputAwareFeedHandler handler = makeFeedHandler(mediaType);
        return new FeedWritable(new FragmentsFeedWriter(feed, handler), mediaType);
    }

    public IVariantAwareWritableRepresentation createSnapshotsFeed(final URI baseURI,
            final IStorage storage, final IConfiguration config,
            final long lastModification, final ICollectionInfo collInfo,
            List<MediaType> feedMediaTypes) throws StorageException, UnsupportedMediaTypeException {
        final SnapshotsFeed feed = new SnapshotsFeed(baseURI.toASCIIString(), 
                                config.getSnapshotFeedTitle(collInfo.getTitle()),
                                lastModification);
        applyAuthorInfo(config, feed, collInfo);
        for (ISnapshotInfo info: storage.getSnapshotInfos(collInfo.getCollectionId())) {
            feed.addEntry(info, LinkUtils.linkTo(baseURI, collInfo, info));
        }
        final MediaType mediaType = getFeedMediaType(feedMediaTypes);
        final IOutputAwareFeedHandler handler = makeFeedHandler(mediaType);
        return new FeedWritable(new SnapshotsFeedWriter(feed, handler), mediaType);
    }

    /**
     * Sets the author information to the specified feed.
     *
     * @param feed The feed.
     */
    private final void applyAuthorInfo(final IConfiguration config, final AbstractFeed<?> feed) {
        feed.addAuthor(config.getAuthor());
    }

    private void applyAuthorInfo(final IConfiguration config, final AbstractFeed<?> feed, final IEntity entity) {
        if (entity instanceof IAuthorAwareEntity) {
            for (IAuthor author: ((IAuthorAwareEntity) entity).getAuthors()) {
                feed.addAuthor(author);
            }
        }
        if (feed.getAuthors().isEmpty()) {
            applyAuthorInfo(config, feed);
        }
    }

    /**
     * Returns a feed handler based on the provided acceptable media types.
     *
     * @param builder A response builder, used to set resulting media type.
     * @param acceptableMediaTypes The preferred media types (provided by a client).
     * @return A feed handler.
     * @throws UnsupportedMediaTypeException 
     */
    private IOutputAwareFeedHandler makeFeedHandler(final MediaType mediaType) throws UnsupportedMediaTypeException {
        IOutputAwareFeedHandler handler = null;
        if (mediaType != null) { 
            handler = FeedHandlerRegistry.createFeedHandler(mediaType);
        }
        if (handler == null) {
            throw new UnsupportedMediaTypeException("", FeedHandlerRegistry.getMediaTypes());
        }
        return handler;
    }

    private static MediaType getFeedMediaType(final List<MediaType> acceptableMediaTypes) {
        return MediaTypeUtils.getPreferredMediaType(FeedHandlerRegistry.getMediaTypes(), acceptableMediaTypes);
    }


    private static final class FeedWritable implements IVariantAwareWritableRepresentation {

        private final MediaType _mediaType;
        private final IFeedWriter _writer;

        FeedWritable(final IFeedWriter writer, final MediaType mediaType) {
            _writer = writer;
            _mediaType = mediaType;
        }

        /* (non-Javadoc)
         * @see com.semagia.atomico.dm.IWritable#write(java.io.OutputStream)
         */
        @Override
        public void write(OutputStream out) throws IOException {
            _writer.write(out);
        }

        /* (non-Javadoc)
         * @see com.semagia.atomico.dm.IWritableRepresentation#getMediaType()
         */
        @Override
        public MediaType getMediaType() {
            return _mediaType;
        }

        /* (non-Javadoc)
         * @see com.semagia.atomico.server.dm.IVariantAwareWritableRepresentation#getVariantMediaTypes()
         */
        @Override
        public List<MediaType> getVariantMediaTypes() {
            return FeedHandlerRegistry.getMediaTypes();
        }
    }

}
